#include "LijolJoystick.h"

#include <assert.h>
#include <cstring>      // for memset
//#include <sys/types.h>
//#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/joystick.h>

#include <sstream>

#include <errno.h>
#include <stdio.h>

/*

#if _WIN32
	#include <Windows.h>
#else
	#include <unistd.h>
	#include <sys/timeb.h>
	#include <time.h>
	#include <sys/time.h>
#endif

// A utility class to provide cross-platform sleep and simple time methods
class Utils
{
public:
	static void sleep( unsigned int _Milliseconds );
	static unsigned long long int getTickFrequency();
	static unsigned long long int getTimeAsTicks();
	static unsigned int getTimeAsMilliseconds();

private:
	static unsigned long long int mInitialTickCount;
};


// Utils class implementation
void Utils::sleep( unsigned int _Milliseconds )
{
#if _WIN32
	::Sleep( _Milliseconds );
#else
	struct timespec l_TimeSpec;
	l_TimeSpec.tv_sec = _Milliseconds / 1000;
	l_TimeSpec.tv_nsec = (_Milliseconds % 1000) * 1000000;
	struct timespec l_Ret;
	nanosleep(&l_TimeSpec,&l_Ret);
#endif
}

unsigned long long int Utils::getTickFrequency()
{
#if _WIN32
	LARGE_INTEGER frequency;
	QueryPerformanceFrequency(&frequency);
	return frequency.QuadPart;
#else
	// The gettimeofday function returns the time in microseconds. So it's frequency is 1,000,000.
	return 1000000;
#endif
}

unsigned long long int Utils::getTimeAsTicks()
{
	unsigned long long int tickCount;
#if _WIN32
	LARGE_INTEGER l;
	QueryPerformanceCounter(&l);
	tickCount = l.QuadPart;
#else
	struct timeval p;
	gettimeofday(&p, NULL);	// Gets the time since the Epoch (00:00:00 UTC, January 1, 1970) in sec, and microsec
	tickCount = (p.tv_sec * 1000LL * 1000LL) + p.tv_usec;
#endif
	if ( mInitialTickCount==0xffffffffffffffffLL )
		mInitialTickCount = tickCount;
	tickCount -= mInitialTickCount;
	return tickCount;
}

unsigned int Utils::getTimeAsMilliseconds()
{
	unsigned int millecondsTime = static_cast<unsigned int>( (getTimeAsTicks() * 1000) / getTickFrequency() );
	return millecondsTime;
}

unsigned long long int Utils::mInitialTickCount = 0xffffffffffffffffLL;
*/
namespace Lijol
{

Joystick::Joystick( const char* device )
    : mDeviceName(device),
	  mJoystickHandle(-1),
      mDriverVersion(0),
      mName(),
      mAxisValues(),
      mButtonValues()
{
	int handle = open( device, O_RDONLY|O_NONBLOCK );
	if ( handle>=0 )
	{
		int driverVersion = 0;
		std::string name;
		char numAxes = 0; 
		char numButtons = 0;
		if ( getJoystickInfo( handle, driverVersion, name, numAxes, numButtons ) )
		{
			mJoystickHandle = handle;
			mDriverVersion = driverVersion;
			mName = name;
			mAxisValues.resize(numAxes, 0);
			mButtonValues.resize(numButtons, false);
		}
		else
		{
			printf("Can't get information for joystick %s\n", device);
			close( handle );
		}
	}
	else
	{
		printf("Can't open joystick %s\n", device);
		return;
	}
}

Joystick::~Joystick()
{
	if ( isValid() )
		close( mJoystickHandle );
}

bool Joystick::getJoystickInfo( int handle, int& driverVersion, std::string& name, char& numAxes, char& numButtons )
{
	driverVersion = 0;
	name = "";
	numAxes = 0;
	numButtons = 0;
	
    if ( ioctl( handle, JSIOCGAXES, &driverVersion )<0 )
		return false;
	
	char nameBuf[256];
    memset( nameBuf, 0, sizeof(nameBuf) );
    if ( ioctl( handle, JSIOCGNAME(sizeof(nameBuf)), nameBuf )<0 )
		return false;
	name = nameBuf;

	if ( ioctl( handle, JSIOCGAXES, &numAxes )<0 )
		return false;
	
    if ( ioctl( handle, JSIOCGBUTTONS, &numButtons )<0 )
		return false;
	
	return true;
}

bool Joystick::isValid() const
{
	return mJoystickHandle!=-1;
}

short int Joystick::getAxisValue( std::size_t axisIndex ) const
{
    if ( !isValid() )
        return 0;
    if ( axisIndex>=mAxisValues.size() )
        return 0;
    return mAxisValues[axisIndex];
}

bool Joystick::getButtonValue( std::size_t buttonIndex ) const
{
    if ( !isValid() )
        return 0;
    if ( buttonIndex>=mButtonValues.size() )
        return 0;
    return mButtonValues[buttonIndex];
}

bool Joystick::update()
{
	if ( !isValid() )
		return false;
	return processEvents();
}

bool Joystick::processEvents()
{
	js_event event;
	bool error = false;
	bool finished = false;
	do
	{
		int bytesRead = read( mJoystickHandle, &event, sizeof(js_event) );
		if ( bytesRead!=-1 )
		{
			assert( bytesRead==sizeof(js_event) );				
			processEvent( event );
		}
		else
		{
			finished = true;
			if ( errno!=EAGAIN )
				error = true;
		}
	}
	while ( !finished );
	
	if ( error )
		return false;
	return true;
}

void Joystick::processEvent( const js_event& event )
{
	if ( event.type & JS_EVENT_BUTTON )
        setButtonValue( event.number, (event.value!=0) );
    else if ( event.type & JS_EVENT_AXIS )
        setAxisValue( event.number, event.value );
}

void Joystick::setAxisValue( std::size_t axisIndex, short int value )
{
    assert(isValid());
    if (axisIndex>=getNumAxes())
        return;
    mAxisValues[axisIndex]=value;
}

void Joystick::setButtonValue( std::size_t buttonIndex, bool value )
{
    assert(isValid());
    if (buttonIndex>=getNumButtons())
        return;
    mButtonValues[buttonIndex]=value;
}

std::string Joystick::toString() const
{
    std::stringstream stream;
    stream << "Joystick:\n";
    stream << "driverVersion:" << getDriverVersion() << "\n";
    stream << "name:" << getName() << "\n";
    for ( std::size_t i=0; i<getNumAxes(); ++i )
        stream << "axis #:" << i << " value:" << getAxisValue(i) << "\n";
    for ( std::size_t i=0; i<getNumButtons(); ++i )
        stream << "button #:" << i << " value:" << getButtonValue(i) << "\n";
    return stream.str();
}

}
